/*
 *  web_server_proxy.c
 *  mongoose
 *
 *  Created by xl on 11-4-24.
 *  Copyright 2011 Justin Lee. All rights reserved.
 *
 */

#import <Foundation/Foundation.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

//////////////////////////////////////get ip////////////////////////////////////////////

#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/sockio.h>
#include <net/if.h>
#include <errno.h>
#include <net/if_dl.h>

#define MAXADDRS    32

#define    min(a,b)    ((a) < (b) ? (a) : (b))
#define    max(a,b)    ((a) > (b) ? (a) : (b))

#define BUFFERSIZE    4000

char *if_names[MAXADDRS] = {0};
char *ip_names[MAXADDRS] = {0};
char *hw_addrs[MAXADDRS] = {0};
unsigned long ip_addrs[MAXADDRS] = {0};

static int   nextAddr = 0;

void InitAddresses()
{
    int i;
    for (i=0; i<MAXADDRS; ++i)
    {
        if_names[i] = ip_names[i] = hw_addrs[i] = NULL;
        ip_addrs[i] = 0;
    }
	nextAddr = 0;
}

void FreeAddresses()
{
    int i;
    for (i=0; i<MAXADDRS; ++i)
    {
        if (if_names[i] != 0) free(if_names[i]);
        if (ip_names[i] != 0) free(ip_names[i]);
        if (hw_addrs[i] != 0) free(hw_addrs[i]);
        ip_addrs[i] = 0;
    }
    InitAddresses();
}

void GetIPAddresses()
{
    int                 i, len, flags;
    char                buffer[BUFFERSIZE], *ptr, lastname[IFNAMSIZ], *cptr;
    struct ifconf       ifc;
    struct ifreq        *ifr, ifrcopy, ifrcopy2;
    struct sockaddr_in    *sin;
    
    char temp[80];
    
    int sockfd;
    
    for (i=0; i<MAXADDRS; ++i)
    {
        if_names[i] = ip_names[i] = NULL;
        ip_addrs[i] = 0;
    }
    
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        perror("socket failed");
        return;
    }
    
    ifc.ifc_len = BUFFERSIZE;
    ifc.ifc_buf = buffer;
    
    if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0)
    {
        perror("ioctl error");
        return;
    }
    
    lastname[0] = 0;
    
    for (ptr = buffer; ptr < buffer + ifc.ifc_len; )
    {
        ifr = (struct ifreq *)ptr;
        len = max(sizeof(struct sockaddr), ifr->ifr_addr.sa_len);
        ptr += sizeof(ifr->ifr_name) + len;    // for next one in buffer
        
        if (ifr->ifr_addr.sa_family != AF_INET)
        {
            continue;    // ignore if not desired address family
        }
        
        if ((cptr = (char *)strchr(ifr->ifr_name, ':')) != NULL)
        {
            *cptr = 0;        // replace colon will null
        }
        
        if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ) == 0)
        {
            continue;    /* already processed this interface */
        }
        
        memcpy(lastname, ifr->ifr_name, IFNAMSIZ);
        
        ifrcopy = *ifr;
        ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy);
        flags = ifrcopy.ifr_flags;
        if ((flags & IFF_UP) == 0)
        {
            continue;    // ignore if interface not up
        }
        /*ioctl(sockfd, SIOCGIFADDR, &ifrcopy2);
        if (ifrcopy2)
        {
        }*/
        
        if_names[nextAddr] = (char *)malloc(strlen(ifr->ifr_name)+1);
        if (if_names[nextAddr] == NULL)
        {
            return;
        }
        strcpy(if_names[nextAddr], ifr->ifr_name);
        
        sin = (struct sockaddr_in *)&ifr->ifr_addr;
        strcpy(temp, inet_ntoa(sin->sin_addr));
        
        ip_names[nextAddr] = (char *)malloc(strlen(temp)+1);
        if (ip_names[nextAddr] == NULL)
        {
            return;
        }
        strcpy(ip_names[nextAddr], temp);
        
        ip_addrs[nextAddr] = sin->sin_addr.s_addr;
        
        ++nextAddr;
    }
    
    close(sockfd);
}

void GetHWAddresses()
{
    struct ifconf ifc;
    struct ifreq *ifr;
    int i, sockfd;
    char buffer[BUFFERSIZE], *cp, *cplim;
    char temp[80];
    
    for (i=0; i<MAXADDRS; ++i)
    {
        hw_addrs[i] = NULL;
    }
    
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        perror("socket failed");
        return;
    }
    
    ifc.ifc_len = BUFFERSIZE;
    ifc.ifc_buf = buffer;
    
    if (ioctl(sockfd, SIOCGIFCONF, (char *)&ifc) < 0)
    {
        perror("ioctl error");
        close(sockfd);
        return;
    }
    
    ifr = ifc.ifc_req;
    
    cplim = buffer + ifc.ifc_len;
    
    for (cp=buffer; cp < cplim; )
    {
        ifr = (struct ifreq *)cp;
        if (ifr->ifr_addr.sa_family == AF_LINK)
        {
            struct sockaddr_dl *sdl = (struct sockaddr_dl *)&ifr->ifr_addr;
            int a,b,c,d,e,f;
            int i;
            
            strcpy(temp, (char *)ether_ntoa(LLADDR(sdl)));
            sscanf(temp, "%x:%x:%x:%x:%x:%x", &a, &b, &c, &d, &e, &f);
            sprintf(temp, "%02X:%02X:%02X:%02X:%02X:%02X",a,b,c,d,e,f);
            
            for (i=0; i<MAXADDRS; ++i)
            {
                if ((if_names[i] != NULL) && (strcmp(ifr->ifr_name, if_names[i]) == 0))
                {
                    if (hw_addrs[i] == NULL)
                    {
                        hw_addrs[i] = (char *)malloc(strlen(temp)+1);
                        strcpy(hw_addrs[i], temp);
                        break;
                    }
                }
            }
        }
        cp += sizeof(ifr->ifr_name) + max(sizeof(ifr->ifr_addr), ifr->ifr_addr.sa_len);
    }
    
    close(sockfd);
}

///////////////////////////////////EOF get ip////////////////////////////////////////////


//////////////////////////////////web server/////////////////////////////////////////////

#include "web_server_proxy.h"
#include "mongoose.h"
#include "../base_utility/base_list.h"

#define DEFAULT_URL_LEN 1024
#define DEFAULT_PORT_LEN 32
#define SUB_STR_LEN 1024

#define DEFAULT_WEB_PATH "Documents/"
#define DEFAULT_PORT "10080"

typedef struct tagWEB_SERVER{
	struct mg_context* pmc;
	char local_url[DEFAULT_URL_LEN];
	char web_dir[DEFAULT_URL_LEN];
	char port[DEFAULT_PORT_LEN];
    int cur_ip_idx;
}WEB_SERVER;

typedef struct tagREQUEST_ID
{
	WEB_SERVER_REQUEST_METHOD method;
	char sub_str[SUB_STR_LEN];
	WEB_SERVER_REQUEST_NOTIFY func;
	void* param;
}REQUEST_ID;

BASE_LIST* p_ws_list = NULL;
int i_ws_list_cnt = 0;

char request_method[WS_RM_NUM][16] = {0};
BASE_LIST* registered_notify[WS_RM_NUM] = {0};

static void init_rm_table()
{
	strcpy(request_method[WS_RM_GET], "GET");
	strcpy(request_method[WS_RM_POST], "POST");
	strcpy(request_method[WS_RM_PUT], "PUT");
	strcpy(request_method[WS_RM_DELETE], "DELETE");
}

static base_bool compare_mg_ctontext(BASE_LIST_CONTENT content, void* param)
{
	WEB_SERVER* node = (WEB_SERVER*)content;
	if (node->pmc == (struct mg_context*)param)
	{
		return base_true;
	}
	return base_false;
}

static void append_to_list(WEB_SERVER* node)
{
	BASE_LIST* list = p_ws_list;
	
	if (p_ws_list == NULL)
	{
		p_ws_list = base_list_create();
        i_ws_list_cnt = 0;
	}
	
	base_list_append(p_ws_list, (BASE_LIST_CONTENT)node);
    i_ws_list_cnt++;
}

static void remove_from_list(WEB_SERVER* node)
{
	base_list_remove(p_ws_list, (BASE_LIST_CONTENT)node);
    i_ws_list_cnt--;
    
    if (i_ws_list_cnt <= 0)
    {
        p_ws_list = NULL;
    }
}

static WEB_SERVER* find_from_list(struct mg_context* pmc)
{
	return (WEB_SERVER*)base_list_conditon_find(p_ws_list, compare_mg_ctontext, (void*)pmc);
}

static base_bool find_sub_str_in_uri(BASE_LIST_CONTENT content, void* param)
{
	REQUEST_ID* rid = (REQUEST_ID*)content;
	if (strstr((char*)param, rid->sub_str))
	{
		return base_true;
	}
	return base_false;
}

static WEB_SERVER_REQUEST_NOTIFY* find_notify(char* method, char* uri, void** p_param)
{
	REQUEST_ID* rid = NULL;
	for(int i = 0; i < WS_RM_NUM; ++i)
	{
		if(strcmp(method, request_method[i]) == 0)
		{
			if (registered_notify[i])
			{
				rid = (REQUEST_ID*)base_list_conditon_find(registered_notify[i], find_sub_str_in_uri, (void*)uri);
				if(rid)
				{
					if (p_param)
					{
						*p_param = rid->param;
					}
					return (WEB_SERVER_REQUEST_NOTIFY*)rid->func;
				}
			}
			else
			{
				if (p_param)
				{
					*p_param = NULL;
				}
				return NULL;
			}

		}
	}
	if (p_param)
	{
		*p_param = NULL;
	}
	return NULL;
}

/*
#define POST_FILE_BUFFER_SIZE (256*1024)

int write_2_file(struct mg_connection *conn)
{
	char file_buffer[POST_FILE_BUFFER_SIZE] = {0};
	char file_path[DEFAULT_URL_LEN] = {0};
	int read_len = 0;
	FILE* hf = NULL;
	[NSHomeDirectory() getCString: file_path];
	strcat(file_path, "/Documents/test.bin");
	hf = fopen(file_path, "wb");
	forward_body_data(conn, hf, -1, NULL);
	fclose(hf);
}
 */

static void* mg_event_deliver(enum mg_event event,
							  struct mg_connection *conn,
							  const struct mg_request_info *request_info)
{
	WEB_SERVER_REQUEST_NOTIFY notify_func = NULL;
	void* notify_param = NULL;
	notify_func = find_notify(request_info->request_method, request_info->uri, &notify_param);
	char new_url[DEFAULT_URL_LEN] = {0};
	if (notify_func)
	{
		strcpy(new_url, request_info->uri);
		notify_func((WEB_SERVER_REQUEST_HANDLE)conn, request_info->remote_ip, request_info->remote_port, notify_param);
		strcpy(request_info->uri, new_url);
	}
	return NULL;
}

static base_bool compare_str(BASE_LIST_CONTENT content, void* param)
{
	REQUEST_ID* rid = (REQUEST_ID*)content;
	if (rid && rid->sub_str && param && strcmp((char*)param, rid->sub_str) == 0)
	{
		return base_true;
	}
	return base_false;
}

void web_server_set_request_callback_func(WEB_SERVER_REQUEST_METHOD method, char* url_sub_str, WEB_SERVER_REQUEST_NOTIFY call_back, void* param)
{
	REQUEST_ID* rid = NULL;
	
	if (registered_notify[method] == NULL)
	{
		registered_notify[method] = base_list_create();
	}
	
	rid = (REQUEST_ID*)base_list_conditon_find(registered_notify[method], compare_str, (void*)url_sub_str);
	
	if (rid)
	{
		rid->func = call_back;
		rid->param = param;
	}
	else
	{
		rid = malloc(sizeof(REQUEST_ID));
		memset(rid, 0, sizeof(REQUEST_ID));
		rid->method = method;
		strcpy(rid->sub_str,url_sub_str);
		rid->func = call_back;
		rid->param = param;
		base_list_append(registered_notify[method], (BASE_LIST_CONTENT)rid);
	}

}

long long web_server_open_post_data(WEB_SERVER_REQUEST_HANDLE hr)
{
	return mg_open_post_data((struct mg_connection *)hr);
}

int web_server_get_post_data_file_name(WEB_SERVER_REQUEST_HANDLE hr, char* buf, int size)
{
    return mg_get_post_data_file_name((struct mg_connection *)hr, buf, size);
}

long long web_server_read_post_data(WEB_SERVER_REQUEST_HANDLE hr, char* buf, int buf_size)
{
	return mg_read_post_data((struct mg_connection *)hr, buf, buf_size);
}

void web_server_close_post_data(WEB_SERVER_REQUEST_HANDLE hr)
{
	mg_close_post_data((struct mg_connection *)hr);
}

WEB_SERVER_HANDLE web_server_start(char* web_dir, char* port)
{
	WEB_SERVER* ws = NULL;
	char default_path[DEFAULT_URL_LEN] = {0};
	char *options[6] = {0};
    int i = 0;
	
	if (request_method[0][0] == 0)
	{
		init_rm_table();
	}
	
	//handle default web directory...
	if (!web_dir)
	{
		[NSHomeDirectory() getCString: default_path];
		strcat(default_path, "/");
		strcat(default_path,DEFAULT_WEB_PATH);
		web_dir = default_path;
	}
	if (!port)
	{
		port = DEFAULT_PORT;
	}
	
	ws = malloc(sizeof(WEB_SERVER));
	memset(ws,0,sizeof(WEB_SERVER));
	strcpy(ws->web_dir, web_dir);
	strcpy(ws->port, port);
	append_to_list(ws);
	
	options[0] = "document_root";
	options[1] = web_dir;
	options[2] = "listening_ports";
	options[3] = port;
	
	//start mongoose...
	ws->pmc = mg_start(mg_event_deliver, options);
	
	if (ws->pmc == NULL)
	{
		remove_from_list(ws);
	}
	
	//Get host url
	FreeAddresses();
	GetIPAddresses();
    for (i = 0; i < nextAddr; ++i)
    {
        if (strstr(if_names[i], "en") != 0)
        {
            ws->cur_ip_idx = i;
            break;
        }
    }
	sprintf(ws->local_url, "http://%s:%s/", ip_names[ws->cur_ip_idx],port);
	//printf(ws->local_url);
	
	return (WEB_SERVER_HANDLE)ws;
}

int web_server_get_server_ip_idx(WEB_SERVER_HANDLE handle)
{
    WEB_SERVER* ws = (WEB_SERVER*)handle;
    if (ws)
    {
        return ws->cur_ip_idx;
    }
    return -1;
}

char* web_server_get_web_dir(WEB_SERVER_HANDLE handle)
{
	WEB_SERVER* ws = (WEB_SERVER*)handle;
	char* ret = NULL;
	if (ws)
	{
		ret = ws->web_dir;
	}
	return ret;
}

char* web_server_get_port(WEB_SERVER_HANDLE handle)
{
	WEB_SERVER* ws = (WEB_SERVER*)handle;
	char* ret = NULL;
	if (ws)
	{
		ret = ws->port;
	}
	return ret;
}

char* web_server_get_local_url(WEB_SERVER_HANDLE handle)
{
	WEB_SERVER* ws = (WEB_SERVER*)handle;
	char* ret = NULL;
	if (ws)
	{
		ret = ws->local_url;
	}
	return ret;
}

void web_server_stop(WEB_SERVER_HANDLE handle)
{
	WEB_SERVER* ws = (WEB_SERVER*)handle;
	if (ws)
	{
		mg_stop(ws->pmc);
		remove_from_list(ws);
		free(ws);
	}
}

int web_server_get_local_ip_address_count()
{
	FreeAddresses();
	GetIPAddresses();
	return nextAddr;
}

char* web_server_get_local_ip_address(int i)
{
	FreeAddresses();
	GetIPAddresses();
    if (i < 0 || i >= nextAddr)
    {
        return "";
    }
	return ip_names[i];
}

int web_server_get_local_hardware_address_count()
{
	return web_server_get_local_ip_address_count();
}

char* web_server_get_local_hardware_address(int i)
{
	FreeAddresses();
	GetIPAddresses();
	GetHWAddresses();
	return hw_addrs[i];
}

void web_server_mg_set_redir_path(char* symbol, char* dir)
{
    mg_set_redir_path(symbol, dir);
}

//////////////////////////////EOF web server/////////////////////////////////////////////